package net.atomarrow.dispatcher;

import net.atomarrow.action.Action;
import net.atomarrow.action.ActionInvocation;
import net.atomarrow.annotation.HttpMethod;
import net.atomarrow.annotation.Inject;
import net.atomarrow.db.enums.Method;
import net.atomarrow.ioc.ActionContext;
import net.atomarrow.mapping.ActionMapping;
import net.atomarrow.upload.MultipartRequest;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpUtils;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;

/**
 * 处理分发
 *
 * @author Michael
 */
public class ProcessDispatcher {
    public static final String MULTIPART = "multipart/";
    @Inject
    private ActionMapping actionMapping;

    public boolean process(HttpServletRequest request,
                           HttpServletResponse response) {
        Action action = actionMapping.findByParseRequest(request);
        if (action == null) {
            return false;
        }
        if (!matchMethod(action, request)) {
            return false;
        }
        MultipartRequest multipartRequest = null;
        if (isMultipartContent(request)) {
            multipartRequest = new MultipartRequest();
            multipartRequest.parseRequest(request);
        }
        processAction(request, response, action, multipartRequest);
        return true;
    }


    private void processAction(HttpServletRequest request,
                               HttpServletResponse response, Action action,
                               MultipartRequest multipartRequest) {
        try {
            ActionContext.init(action, request, multipartRequest, response);
            processAction(action, request);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            ActionContext.destory();
        }
    }


    private void processAction(Action action, HttpServletRequest request) {
        new ActionInvocation(action).doInvoke();
        if (ActionContext.getContext().hasNextAction()) {
            String url = processMappingUrl(ActionContext.getContext().getNextAction());
            action = actionMapping.findByParseUrl(url, request);
            if (action == null) {
                throw new RuntimeException("no mapping for " + ActionContext.getContext().getNextAction());
            }
            ActionContext.getContext().clearNextAction();
            processAction(action, request);
        }
    }

    private String processMappingUrl(String nextAction) {
        int index = nextAction.indexOf("?");
        if (index != -1) {
            String queryString = nextAction.substring(index + 1);
            parseParamToRequest(queryString);
            return nextAction.substring(0, index);
        }
        return nextAction;
    }

    private void parseParamToRequest(String queryString) {
        Hashtable queryParameters = HttpUtils.parseQueryString(queryString);
        Iterator<Map.Entry> iter = queryParameters.entrySet().iterator();
        while (iter.hasNext()) {
            Map.Entry<String, String[]> entry = iter.next();
            String[] values = entry.getValue();
            ActionContext.getContext().getRequest().setAttribute(entry.getKey(), values[0]);
        }
    }

    private boolean matchMethod(Action action, HttpServletRequest request) {
        boolean appointMethod = action.getMethod().isAnnotationPresent(HttpMethod.class);
        if (!appointMethod) {
            return true;
        }
        HttpMethod httpMethod = action.getMethod().getAnnotation(HttpMethod.class);
        if (request.getMethod().equals("POST")) {
            return httpMethod.value() == Method.POST;
        } else if (request.getMethod().equals("GET")) {
            return httpMethod.value() == Method.GET;
        } else if (request.getMethod().equals("DELETE")) {
            return httpMethod.value() == Method.DELETE;
        } else if (request.getMethod().equals("PUT")) {
            return httpMethod.value() == Method.PUT;
        } else if (request.getMethod().equals("HEAD")) {
            return httpMethod.value() == Method.HEAD;
        } else if (request.getMethod().equals("OPTIONS")) {
            return httpMethod.value() == Method.OPTIONS;
        } else if (request.getMethod().equals("TRACE")) {
            return httpMethod.value() == Method.TRACE;
        }
        return false;
    }

    public boolean isMultipartContent(HttpServletRequest request) {
        if (!request.getMethod().equals("POST")) {
            return false;
        }
        String contentType = request.getContentType();
        if (contentType == null) {
            return false;
        }
        return contentType.toLowerCase(Locale.ENGLISH).startsWith(MULTIPART);
    }


}
